package com.mfin.persistence;

import java.io.Serializable;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class PersistenceGovernor implements IPersistenceGovernor {

	protected static final String NULL_SESSION_FACTORY = "Hibernate session factory not initialized";
	protected static final String NULL_SESSION = "Hibernate session must be initialized";
	private SessionFactory hibernateSessionFactory;
	private static PersistenceGovernor persistenceGovernor;
	protected IPersistenceExceptionManager persistenceExceptionManager = new HibernateExceptionManager();

	ThreadLocal<Session> hibernateSession = new ThreadLocal<Session>();
	ThreadLocal<Transaction> hibernateTransaction = new ThreadLocal<Transaction>();

	private PersistenceGovernor() {
		super();

		try {
			Configuration configuration = new Configuration();
			configuration.configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(configuration.getProperties())
					.buildServiceRegistry();
			hibernateSessionFactory = configuration
					.buildSessionFactory(serviceRegistry);
		} catch (HibernateException hex) {
			PersistenceException pe = new PersistenceException(hex.getMessage());
			pe.setCause(hex);
			throw pe;
		}
	}

	public static PersistenceGovernor getInstance() {
		synchronized (PersistenceGovernor.class) {
			if (persistenceGovernor == null) {
				persistenceGovernor = new PersistenceGovernor();
			}
		}
		return persistenceGovernor;
	}

	@Override
	public void init() {
		try {

			if (hibernateSessionFactory == null) {
				throw new PersistenceException(NULL_SESSION_FACTORY);
			}

			hibernateSession.set(hibernateSessionFactory.openSession());
			hibernateTransaction.set(getHibernateSession().beginTransaction());
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
	}

	public Session getHibernateSession() {
		return hibernateSession.get();
	}

	public Transaction getHibernateTransaction() {
		return hibernateTransaction.get();
	}

	@Override
	public void close() throws PersistenceException {
		close(true);
	}

	@Override
	public void close(boolean commit) throws PersistenceException {

		try {
			if (commit) {
				if (getHibernateSession() != null)
					getHibernateSession().flush();
				if (getHibernateTransaction() != null)
					getHibernateTransaction().commit();
			} else {
				if (getHibernateTransaction() != null)
					getHibernateTransaction().rollback();
			}
		} catch (Exception ex) {
			getPersistenceExceptionManager().handle(ex);
		} finally {
			try {
				if (getHibernateSession() != null)
					getHibernateSession().close();
			} catch (Exception ex) {
				getPersistenceExceptionManager().handle(ex);
			}
		}
		hibernateSession.set(null);
		hibernateTransaction.set(null);
	}

	@Override
	public Serializable insert(Object obj) throws PersistenceException {

		if (hibernateSessionFactory == null) {
			throw new PersistenceException(NULL_SESSION_FACTORY);
		}
		Serializable id = null;
		try {
			if (getHibernateSession() != null)
				id = getHibernateSession().save(obj);
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
		return id;
	}

	@Override
	public void persist(Object obj) throws PersistenceException {
		if (getHibernateSession() == null) {
			throw new PersistenceException(NULL_SESSION);
		}
		try {
			if (getHibernateSession() != null)
				getHibernateSession().save(obj);
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
	}

	@Override
	public void update(Object obj) throws PersistenceException {
		if (getHibernateSession() == null) {
			throw new PersistenceException(NULL_SESSION);
		}
		try {
			if (getHibernateSession() != null)
				getHibernateSession().update(obj);
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
	}

	@Override
	public void delete(Object obj) throws PersistenceException {
		if (getHibernateSession() == null) {
			throw new PersistenceException(NULL_SESSION);
		}
		try {
			if (getHibernateSession() != null)
				getHibernateSession().delete(obj);
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
	}

	@Override
	public Object get(Class<?> clazz, Serializable objKey)
			throws PersistenceException {

		if (getHibernateSession() == null) {
			throw new PersistenceException(NULL_SESSION);
		}
		Object retObj = null;
		try {
			if (getHibernateSession() != null) {
				retObj = getHibernateSession().get(clazz, objKey);
			}
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
		return retObj;

	}

	@Override
	public List createCriteria(Class<?> clazz) throws PersistenceException {

		if (getHibernateSession() == null) {
			throw new PersistenceException(NULL_SESSION);
		}
		List retObjList = null;
		try {
			if (getHibernateSession() != null) {
				retObjList = getHibernateSession().createCriteria(clazz).list();
			}
		} catch (PersistenceException pex) {
			getPersistenceExceptionManager().handle(pex);
		}
		return retObjList;

	}

	public IPersistenceExceptionManager getPersistenceExceptionManager() {
		return persistenceExceptionManager;
	}
}
